#!/usr/bin/env python
# encoding: utf-8
#
# Copyright 2009-2011 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#      http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
makecatalogs

Created by Greg Neagle on 2009-03-30.

Recursively scans a directory, looking for installer item info files.  
Builds a repo catalog from these files.

Assumes a pkgsinfo directory under repopath.
User calling this needs to be able to write to repo/catalogs.

"""

import sys
import os
import optparse

try:
    from munkilib import FoundationPlist as plistlib
except ImportError:
    try:
        import FoundationPlist as plistlib
    except ImportError:
        # maybe we're not on an OS X machine...
        print >> sys.stderr, ("WARNING: FoundationPlist is not available, "
                              "using plistlib instead.")
        import plistlib
        
try:
    from munkilib.munkicommon import listdir, get_version
except ImportError:
    # munkilib is not available
    def listdir(path):
        """OSX HFS+ string encoding safe listdir().

        Args:
            path: path to list contents of
        Returns:
            list of contents, items as str or unicode types
        """
        # if os.listdir() is supplied a unicode object for the path,
        # it will return unicode filenames instead of their raw fs-dependent
        # version, which is decomposed utf-8 on OSX.
        #
        # we use this to our advantage here and have Python do the decoding
        # work for us, instead of decoding each item in the output list.
        #
        # references:
        # http://docs.python.org/howto/unicode.html#unicode-filenames
        # http://developer.apple.com/library/mac/#qa/qa2001/qa1235.html
        # http://lists.zerezo.com/git/msg643117.html
        # http://unicode.org/reports/tr15/    section 1.2
        if type(path) is str:
            path = unicode(path, 'utf-8')
        elif type(path) is not unicode:
            path = unicode(path)
        return os.listdir(path)
        
    def get_version():
        '''Placeholder if munkilib is not available'''
        return 'UNKNOWN'
        
def makecatalogs(repopath):
    '''Assembles all pkginfo files into catalogs.
    Assumes a pkgsinfo directory under repopath.
    User calling this needs to be able to write to repo/catalogs.'''
    pkgsinfopath = os.path.join(repopath, 'pkgsinfo')
    if not os.path.exists(pkgsinfopath):
        print >> sys.stderr, "pkgsinfo path %s doesn't exist!" % pkgsinfopath
        exit(-1)
    
    catalogs = {}
    catalogs['all'] = []    
    for dirpath, dirnames, filenames in os.walk(pkgsinfopath):
        #subdir = dirpath[len(pkgsinfopath):]
        for name in filenames:
            if name.startswith("._") or name == ".DS_Store":
                # don't process these
                continue
                
            filepath = os.path.join(dirpath, name)
            
            try:
                pkginfo = plistlib.readPlist(filepath)
            except IOError, inst:
                print >> sys.stderr, ("IO error for %s: %s" % 
                                        (filepath, inst))
                continue
            except Exception, inst:
                print >> sys.stderr, ("Unexpected error for %s: %s" % 
                                        (filepath, inst))
                continue
            
            #simple sanity checking
            if not 'installer_item_location' in pkginfo:
                print >> sys.stderr, \
                    ("WARNING: file %s is missing installer_item_location" % 
                                                                    filepath)
                continue
                
            installeritempath = os.path.join(repopath, "pkgs",
                            pkginfo['installer_item_location'])
            if not os.path.exists(installeritempath):
                print >> sys.stderr, ("WARNING: Info file %s refers to "
                                      "missing installer item: %s" % 
                                        (filepath[len(pkgsinfopath)+1:],
                                        pkginfo['installer_item_location']))
                continue
                
            catalogs['all'].append(pkginfo)
            for catalogname in pkginfo.get("catalogs", []):
                if not catalogname in catalogs:
                    catalogs[catalogname] = []
                catalogs[catalogname].append(pkginfo)
                print "Adding %s to %s..." % \
                    (filepath[len(pkgsinfopath)+1:], catalogname)
               
    # clear out old catalogs
    catalogpath = os.path.join(repopath, "catalogs")
    if not os.path.exists(catalogpath):
        os.mkdir(catalogpath)
    else:
        for item in listdir(catalogpath):
            itempath = os.path.join(catalogpath, item)
            if os.path.isfile(itempath):
                os.remove(itempath)
    
    # write the new catalogs
    for key in catalogs.keys():
        catalogpath = os.path.join(repopath, "catalogs", key)
        plistlib.writePlist(catalogs[key], catalogpath)
        

def main():
    '''Main'''
    usage = "usage: %prog [options] /path/to/repo_root"
    p = optparse.OptionParser(usage=usage)
    p.add_option('--version', '-V', action='store_true',
                      help='Print the version of the munki tools and exit.')
    options, arguments = p.parse_args()
    
    if options.version:
        print get_version()
        exit(0)
    
    if len(arguments) == 0:
        print >> sys.stderr, "Need to specify a path to the repo root!"
        exit(-1)
        
    repopath = arguments[0].rstrip("/")
    if not os.path.exists(repopath):
        print >> sys.stderr, "Repo root path %s doesn't exist!" % repopath
        exit(-1)
            
    makecatalogs(repopath)

if __name__ == '__main__':
    main()

